/* Copyright (c) 2009-2010, Sean A.O. Harney <sharney3@gmail.com> */

/* most of this file was moved out of the yacc file, so that it would be easier to C indent */

#include "omf.h"


/* prototypes */
bool _parse(void);
extern void yyrestart(FILE * input_file);
extern int yyparse();
//yacc/lex globals
extern int yylineno;
extern char *yytext;
extern FILE *yyin;

/* globals */
//TODO; move to glob in omf.h
LLIST_P _current_func_asl;	/* used at parse-time. `current` meaning where yacc is at in the grammar */
/* for building the current function's ASL */


void yyerror(const char *err)
{
    char str[256];
    char *filename = glob.parser_current_filename;

    if (filename == NULL)	/* because it is parsing stdin */
	str[0] = '\0';
    else
	snprintf(str, sizeof(str), "%s:", filename);

    fprintf(stderr, "%s%d: `%s` near token '%s'\n", str, yylineno, err,
	    yytext);
    glob.parser_num_errors++;
}

void yyerror_println(const char *fmt, ...)
{
    va_list argp;
    char strerr[512];


    va_start(argp, fmt);	//fmt is last before the elipsis ...
    vsnprintf(strerr, sizeof(strerr), fmt, argp);
    va_end(argp);
    yyerror(strerr);
}

void yyerror_fatal(const char *err)
{
    yyerror(err);
    fatal_error();
}

void yyerror_println_fatal(const char *fmt, ...)
{
    va_list argp;
    char strerr[512];


    va_start(argp, fmt);	//fmt is last before the elipsis ...
    vsnprintf(strerr, sizeof(strerr), fmt, argp);
    va_end(argp);
    yyerror(strerr);
    fatal_error();
}

bool parse_init(FILE * fp, bool parseonly)
{
    current_func_asl_clear();
    yyin = fp;			//set yyin to the given fp
    yylineno = 1;
    /*
       if (fileno(yyin) != fileno(stdin)) //only prevalidate for script files
       {
       validation_mode = true;
       debug_unquiet_printf("Validating syntax...\n");
       if (_parse() == false)
       return false;
       debug_unquiet_printf("Syntactically valid program.\n");
       atexit_init();     // the time printout. TODO: move to after start perhaps.

       glob.omf_state
       ADD STATE_RESET

       if (fileno(yyin) != fileno(stdin))       //only prevalidate for script files
       {
       rewind(yyin);            // rewind input and parse again
       yyrestart(yyin);
       }
     */
    if (fileno(yyin) != fileno(stdin))
	debug_unquiet_printf
	    ("Parsing, verifying syntax and building Abstract Statement List(s)...\n");

    if (!_parse())
      {
	  fprintf(stderr,
		  "Parse failed. Syntactically invalid program.\n");
	  return false;
      }

    if (fileno(yyin) != fileno(stdin))
	debug_unquiet_printf("Syntactically valid program.\n");

    if (debug_level >= 2 || parseonly)
      {
	  if (parseonly && debug_level < 2)
	      debug_level = 2;	/* -P and debuglevel<2 => debug level of 2 */
	  asl_print_all(debug_file);
      }
    return true;
}

bool _parse(void)
{
    if (yyparse() != 0 || glob.parser_num_errors > 0)
      {
	  error_println("%d Syntax error(s).\n", glob.parser_num_errors);
	  return false;
      }
    return true;

}

void current_func_asl_clear(void)
{
    _current_func_asl = NULL;
    /* TODO: maybe in future do a shallow free'ing of some heap ptrs regarding llist */
}

/* will create list if it has not been created yet.*/
void current_func_asl_add(ASL_NODE_P stmt)
{
    /* NOTE: must append, not prepend! */
    _current_func_asl = asl_append(_current_func_asl, stmt);
}

/* done at parse-time.the yacc action for Function_Decl */
void function_decl(char *symname, LLIST_P asl)
{

    if (!strcmp(symname, "main"))
      {
	  SYMTBL_ENT_P entry =
	      symbol_tbl_get_entry(glob.symbol_tbl_global, "main");
	  if (entry != NULL)	/* exists already,main was declared implicitly */
	    {
		//TODO: assert entry type is func
		LLIST_P asl_main = entry->u.function.asl;

		if (glob.parser_explicit_main == true)
		    yyerror
			("main was already explicitly declared. Cannot explicitly declare a function more than once");
		else
		  {
		      LLIST_P walker;
		      ASL_NODE_P stmt;

		      debug2_printf
			  ("main was already implicitly declared. Combining impicit and explicit ASLs\n");
		      /* **append** statements stmts to main */
		      for (walker = asl; walker != NULL;
			   walker = walker->next)
			{
			    stmt = walker->element;
			    asl_main = asl_append(asl_main, stmt);
			}
		  }
	  } else
	    {
		glob.parser_explicit_main = true;
		debug2_printf("Adding new ASL [%s]\n", "main");
		if (!symbol_tbl_new_entry_function
		    (glob.symbol_tbl_global, "main",
		     glob.parser_current_filename, asl))
		    yyerror_println
			("symbol_tbl add entry failed. function: %s",
			 "main");
	    }
    } else
      {

	  if (symbol_tbl_get_entry(glob.symbol_tbl_global, symname) !=
	      NULL)
	      yyerror_println
		  ("%s was already explicitly declared. Cannot explicitly declare a function more than once",
		   symname);
	  else
	    {
		debug2_printf("Function_Decl: Adding new ASL [%s]\n",
			      symname);
		if (!symbol_tbl_new_entry_function
		    (glob.symbol_tbl_global, symname,
		     glob.parser_current_filename, asl))
		    yyerror_println
			("symbol_tbl add entry failed. function: %s",
			 symname);
	    }
      }
}



/* All statements not contained in an (explicitly declared) function are prepened to main.
 * come before (on a previous line, or sharing a line) the first function
 * declaration. IOW:
 * 	0 or more stmts, or 0 or more stmts and 0 or more func declarations.
 * This is enforced by the grammar directly. */

//NOTE: this is the action for StmtsAndFuncs yacc rule
void implicit_main_stmts(LLIST_P asl)
{
    /* ASL is full of stmts which are not in any function. */
    //TODO: make check exists bool symboltbl func
    if (symbol_tbl_get_entry(glob.symbol_tbl_global, "main") == NULL)
      {
	  //if it doesn't exist
	  debug2_printf("StmtsAndFuncs: Adding new ASL [%s]\n", "main");
	  if (!symbol_tbl_new_entry_function
	      (glob.symbol_tbl_global, "main",
	       glob.parser_current_filename, asl))
	      yyerror_println("symbol_tbl add entry failed. function: %s",
			      "main");

    } else
      {
	  /* doesn't happen. Grammar enforces that main will be declared implicitly before explicitly, or solely
	   * explicitly , or solely implicitly*/
	  yyerror
	      ("Somehow main declared explicitly before implicitly. This is impossible!");
      }
    current_func_asl_clear();
}
